
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define EPOLL_EVENTS 100

int handle(int con, uint32_t events);

int net_listen(int port);

int set_no_blocking(int fd);

int main() {
    //epoll
    int epoll = epoll_create(100);
    if (epoll == -1) {
        printf("epoll_create error\n");
        return -1;
    }
    printf("epoll_create success\n");
    // listen tcp on :8080
    int sock = net_listen(8080);
    if (sock < 0) {
        printf("net_listen error\n");
        return -1;
    }
    struct epoll_event ev, events[EPOLL_EVENTS];
    ev.events = EPOLLIN;
    ev.data.fd = sock;
    epoll_ctl(epoll, EPOLL_CTL_ADD, sock, &ev);

    int fd;
    while (1) {
        int n = epoll_wait(epoll, events, EPOLL_EVENTS, -1);
        printf("new events: %d\n", n);
        for (int i = 0; i < n; ++i) {
            fd = events[i].data.fd;
            if (fd == sock) {
                // accept
                struct sockaddr_in client_addr;
                socklen_t client_addr_len = sizeof(client_addr);
                int conn = accept(sock, (struct sockaddr *) &client_addr, &client_addr_len);
                if (conn < 0) {
                    printf("accept error\n");
                    return -1;
                }
                set_no_blocking(conn);
                printf("accept success %d, %d\n", client_addr.sin_addr.s_addr, client_addr.sin_port);
                ev.events = EPOLLIN | EPOLLOUT;
                ev.data.fd = conn;
                epoll_ctl(epoll, EPOLL_CTL_ADD, conn, &ev);
            } else {
                if (handle(fd, events[i].events) == -1) {
                    ev.events = EPOLLIN | EPOLLOUT;
                    ev.data.fd = fd;
                    epoll_ctl(epoll, EPOLL_CTL_DEL, fd, &ev);
                }
            }
        }
    }
}

int net_listen(int port) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        printf("socket create err %d\n", sock);
        return -1;
    }
    printf("sock success sfd=%d\n", sock);
    int reuse = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
        printf("setsockopt error\n");
        return -1;
    }

    //bind
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (const struct sockaddr *) &addr, sizeof(addr)) < 0) {
        printf("bind error\n");
        return -1;
    }
    printf("bind success\n");
    //listen
    if (listen(sock, 100) < 0) {
        printf("listen error\n");
        return -1;
    }
    printf("listen success\n");
    return sock;
}

int handle(int conn, uint32_t events) {
    char readBuf[1024];
    char *res = "HTTP/1.1 200\r\n"
                "Content-Length: 11\r\n\r\n"
                "Hello World";
    long n;
    if (events & EPOLLIN) {
        //read
        bzero(readBuf, 1024);
        n = recv(conn, readBuf, sizeof(readBuf), 0);
        if (n <= 0) {
            if (n == 0) {
                printf("sock close\n");
            } else {
                printf("read err, sock force close\n");
            }
            close(conn);
            return -1;
        }
        printf("read %s\n", readBuf);
    } else if (events & EPOLLOUT) {
        send(conn, res, strlen(res), 0);
        printf("send res\n");
    } else {
        printf("unknow event %d\n", events);
        return -1;
    }
    return 0;
}


int set_no_blocking(int fd) {
    /* Change the socket into non-blocking state F_SETFL is a command saying set flag and flag is 0_NONBLOCK */
    const int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) {
        return flags;
    }
    if (flags & O_NONBLOCK) {
        return 0;
    }
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
